//
// Created by chizuru on 2025/9/2.
//

#include "DeviceComm.h"
#include "xcliberr.h"
#include "IConfigurable.h"
#include "OperationResult.h"

#include <mutex>
#include <vector>
#include <iostream>
#include <chrono>
#include <thread>
using std::mutex;
using std::lock_guard;
using std::vector;
using std::cerr;
using std::chrono::steady_clock;
using std::thread;
using std::optional;
using std::hex;
using std::chrono::milliseconds;
using std::this_thread::sleep_for;

DeviceComm::DeviceComm(IDeviceIO& device) : device(device) {}

OperationResult DeviceComm::configureSerial(std::any baud, std::any dataBits, std::any parity, std::any stopBits, std::any flowctrl) {
    lock_guard lk(mtx);
    if (auto cfg = dynamic_cast<IConfigurable*>(&device)) {
        int r = cfg->configure(baud, dataBits, parity, stopBits, flowctrl);
        if (r < 0) {
            return OperationResult::Fail(ErrorCode::IOError, "configure failed");;
        }
        return OperationResult::Ok();
    }
    return OperationResult::Fail(ErrorCode::Unsupported, "wrapper not configurable");
}

bool DeviceComm::flush() {
    lock_guard lk(mtx);
    return device.flushReadAndWrite() >= 0;
}

OperationResult DeviceComm::sendFrameAndReadResponse(const vector<uint8_t>& frame,
                                                     const IResponseTerminator& terminator,
                                                     vector<uint8_t>& outResp,
                                                     std::chrono::milliseconds duration,
                                                     std::chrono::milliseconds timeoutMs,
                                                     std::chrono::milliseconds interReadDelay) {
    using namespace std::chrono_literals;
    lock_guard lk(mtx);
    // flush
    if (device.flushReadAndWrite() < 0) {
        return OperationResult::Fail(ErrorCode::IOError, "flush failed");
    }

    // write
    int w = device.write( reinterpret_cast<const char*>(frame.data()), frame.size());
    if (w < 0) {
        return OperationResult::Fail(ErrorCode::IOError, "write failed");
    }
    std::this_thread::sleep_for(duration);

    outResp.clear();
    auto start = steady_clock::now();

    constexpr size_t BUF_SZ = 256;
    std::vector<uint8_t> tmp(BUF_SZ);

    while (true) {
        int r = device.read( reinterpret_cast<char*>(tmp.data()), tmp.size());
        if (r < 0) {
            return OperationResult::Fail(ErrorCode::IOError, "read error");
        } else if (r == 0) {
            auto now = steady_clock::now();
            if (now - start >= timeoutMs) {
                return OperationResult::Fail(ErrorCode::Timeout, "read timeout");
            }
            sleep_for(milliseconds(interReadDelay));
            continue;
        } else {
            outResp.insert(outResp.end(), tmp.begin(), tmp.begin() + r);

            // let the terminator decide if it's done
            if (terminator.isComplete(outResp)) {
                return OperationResult::Ok();
            }

            // otherwise continue until timeout
            auto now = std::chrono::steady_clock::now();
            if (now - start >= timeoutMs) {
                return OperationResult::Fail(ErrorCode::Timeout, "read timeout before terminator signaled completion");
            }
        }
    }
}
